/*   
 * Copyright 2010 Rob Schoening
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *    
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.beaconframework.vaadin.data;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Set;

import org.beaconframework.vaadin.data.provider.AbstractItemContainer;
import org.beaconframework.vaadin.data.provider.AbstractReadOnlyItem;
import org.beaconframework.vaadin.data.provider.AbstractReadOnlyProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vaadin.data.Item;
import com.vaadin.data.Property;

public class ReflectionItemContainer<BT> extends AbstractItemContainer<BT> {

    /**
     * 
     */

    private static Logger log = LoggerFactory
            .getLogger(ReflectionItemContainer.class);
    private static final long serialVersionUID = 3898913234901927294L;

    private Class<? extends BT> modelClass;

    private HashMap<String, PropertyDescriptor> descriptorMap = null;


    public ReflectionItemContainer(Class<? extends BT> claz) {
        super();
        modelClass = claz;
        introspect(claz);

    }


    public void addBean(BT bean) {
        addItem(bean);
    }


    void introspect(final Class<?> beanClass) {
        final LinkedHashMap<String, PropertyDescriptor> pdMap = new LinkedHashMap<String, PropertyDescriptor>();

        // Try to introspect, if it fails, we just have an empty Item
        try {
            final BeanInfo info = Introspector.getBeanInfo(beanClass);
            final PropertyDescriptor[] pds = info.getPropertyDescriptors();

            // Add all the bean properties as MethodProperties to this Item
            for (int i = 0; i < pds.length; i++) {
                final Method getMethod = pds[i].getReadMethod();
                if ((getMethod != null)
                        && getMethod.getDeclaringClass() != Object.class) {
                    pdMap.put(pds[i].getName(), pds[i]);
                    addContainerProperty(pds[i].getName(), pds[i]
                            .getPropertyType(), null);
                }
            }
        } catch (final java.beans.IntrospectionException ignored) {
            log.warn("", ignored);
        }

        descriptorMap = pdMap;
    }


    @Override
    protected Item newItem(final BT pojo) {

        Set<Object> s = new HashSet<Object>();
        s.addAll(descriptorMap.keySet());
        return new AbstractReadOnlyItem(pojo, s) {

            /**
             * 
             */
            private static final long serialVersionUID = 241932618318484736L;


            @Override
            public Property getItemProperty(final Object id) {
                // TODO Auto-generated method stub
                return new AbstractReadOnlyProperty(this, id) {

                    /**
                     * 
                     */
                    private static final long serialVersionUID = -8464032132743916132L;


                    @Override
                    public Object getValue() {
                        PropertyDescriptor pd = descriptorMap.get(id);
                        if (pd == null) {
                            return null;
                        }
                        try {
                            return pd.getReadMethod().invoke(pojo);
                        } catch (Exception e) {
                            log.warn("", e);
                        }
                        return null;
                    }


                    @Override
                    public Class<?> getType() {
                        // TODO Auto-generated method stub
                        PropertyDescriptor pd = descriptorMap.get(id);
                        if (pd == null) {
                            return null;
                        }
                        return pd.getPropertyType();
                    }
                };
            }
        };
    }

}
